// void, Obj me, Obj tgt
Unit this;
Obj o;
ObjList ol, olBall;
int bc, csc, disease_cost;
int ranget, rangep, i, p, k;
int per_health, per_stamina, min;
str class;
bool possibleSpeed;

Sleep(rand(100)+30);
class = tgt.class;  
this = me.AsUnit(); 

if (.IsValid) {// leave parry mode at new command
	.SetParryMode(false);
	possibleSpeed = false;
	if(.IsMilitary()){
		if(!.IsHeirOf("Hero")
		&& !.IsHeirOf("Horse")
		&& !.IsHeirOf("RangedHorse")
		&& !.IsHeirOf("RamUnit")
		&& !.IsHeirOf("Sentry")
		&& !.HasFreedom){
			possibleSpeed = true;
		}
	}
	if(.item_count > 0){
		Item item;
		
		item = .FindItem("Terrain bonus I");
		if(item.IsValid)
			.RemoveItemsOfType("Terrain bonus I");
		else {
			item = .FindItem("Terrain bonus II");
			if(item.IsValid)
				.RemoveItemsOfType("Terrain bonus II");
			else {
				item = .FindItem("Terrain bonus III");
				if(item.IsValid)
					.RemoveItemsOfType("Terrain bonus III");
				else {
					item = .FindItem("Terrain bonus IV");
					if(item.IsValid)
						.RemoveItemsOfType("Terrain bonus IV");
				}
			}
		}
	}
}

bc = GetConst("BallCircle");
csc = GetConst("CurseStaminaCost");
disease_cost = GetConst("DiseaseCost");

if(.IsHeirOf("EChariot") && .stamina >= disease_cost){
	o = .BestTargetInSquadSight_PreferUndiseased();
} else {
	o = .BestTargetInSquadSight(class);
}
if(.IsHeirOf("Horse")){
	if(o.AsCatapult.IsHeirOf("Catapult"))
		return;
} else {
	if(o.AsCatapult.IsHeirOf("CCatapult"))
		return;
}
if(.IsValidTarget(o))
if(.DistTo(o.pos) > .min_range){
	// @@@@@@@@@@@@@@@@@@@@@@@@@@@ pr("engage: target found!");
	while (o.IsAlive() && .IsValidTarget(o) && (.DistTo(o.pos) > .min_range)) {

		Sleep(rand(100) + 30);// Disperse the times 
                        
        if(.IsRanged){
			if(.IsHeirOf("EChariot") && .stamina >= disease_cost){
				o = .BestTargetInSquadSight_PreferUndiseased();
			} else {
				o = .BestTargetInSquadSight(class);
			}
		}
		// ######## MODUL UNIT SPEED START ##############
		
		if(k > 1)
		if(possibleSpeed){
			k = 0;		
			per_health = (.health+1)*100/(.maxhealth+1);

			per_stamina = (.stamina+1)*100/(.maxstamina+1);
			
			min = MIN(per_health, per_stamina);
			
			if(min < 80){
				if(min > 50) { min = 50; 
				} else if(min < 30) { min = 30;
				} else { min = 40; }
				
				if(.speed_factor != min)
					.SetSpeedFactor(min);
					
			} else {
				if(.speed_factor < 100)                                  
					.SetSpeedFactor(100);
			} 
		}
		k+=1;
		
		// ######## MODUL UNIT SPEED END ##############
		// @@@@@@@@@@@@@@@@@@@@@@@@@@@ pr("engage: approaching target!");
		while(!.GotoAttack(o, 1500, true, 600000)) {
		
			//################### MODUL RETREAT 2 START ####################################
	
			if(o.AsUnit.hero.IsValid){
				if(o.AsUnit.hero.command == "retreat" ||
				   o.AsUnit.hero.command == "retreat_notai" ||
				   o.AsUnit.command == "retreat_bld_notai"){
					Sleep(rand(100)+100);
					break;
				}
			} else {
				if(o.AsUnit.command == "retreat" ||
				   o.AsUnit.command == "retreat_notai" ||
				   o.AsUnit.command == "retreat_bld_notai"){
					Sleep(rand(100)+100);
					break;
				}
			}
			//################### MODUL RETREAT 2 END ####################################
				
			if(.IsRanged){
				
				if(.IsHeirOf("EChariot") && .stamina >= disease_cost){
					o = .BestTargetInSquadSight_PreferUndiseased();
				} else {
					o = .BestTargetInSquadSight(class);
				}
				
				if(!o.IsAlive() || !.IsValidTarget(o) || (.DistTo(o.pos) < .min_range)) break;
			} else {
				i+=1;
					
				if(i > 2){
					if(!.IsVisible){
						.SetLastAttackTime();
						.SetVisible(true); 
					}
				} 
				
				if(!o.IsAlive() || !.IsValidTarget(o) || (.DistTo(o.pos) < .min_range)) break;
				
				ranget = .DistTo(o.pos); // odleglosc od celu 
					
				if(ranget > 150){
					rangep = 20+p;
				  
					o = .BestTargetInRange( .posRH, ranget+rangep );
					
				} else {
					if(i >= 10){
						ol = Intersect(ObjsInCircle(.posRH, 60, "Military"), EnemyObjs(.player, "Military")).GetObjList;
						if(ol.count > 0)
							o = .BestTargetInRange( .posRH, 60 );  
					}
				}
				p+=5;
			} 
		}
		// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@ pr("engage: attacking target!");
		if (o.IsAlive() && .IsValidTarget(o) && (.DistTo(o.pos) > .min_range)) {
		
			while (.Attack(o)){
 
				if(.IsAlive)
				if(o.AsUnit.IsAlive)
				if(.DistTo(o.pos) > .min_range){
					// ######## MODUL UNIT SPEED START ##############
					
					if(k > 1)
					if(possibleSpeed){
						k = 0;
						per_health = (.health+1)*100/(.maxhealth+1);

						per_stamina = (.stamina+1)*100/(.maxstamina+1);
						
						min = MIN(per_health, per_stamina);
						
						if(min < 80){
							if(min > 50) { min = 50; 
							} else if(min < 30) { min = 30;
							} else { min = 40; }
							
							if(.speed_factor != min)
								.SetSpeedFactor(min);
								
						} else {
							if(.speed_factor < 100)                                  
								.SetSpeedFactor(100);
						} 
					}
					k+=1;
					// ######## MODUL SHIP 1 START ##############
					if(.IsHeirOf("ShipBattle")){
						if(.stamina >= 1)
							.SetStamina(.stamina - 1);
								
						if(.stamina <= 0)
							Sleep(10000);
						continue;
					}
					// ######## MODUL SHIP 1 END ################
					// ######## MODUL UNIT SPEED END ##############
					if(.HasSpecial(curse)){
						if(!o.AsUnit.IsCursed){
							if(.stamina >= csc){
								if(.IsAlive)
									if(o.AsUnit.IsAlive){
										.SetStamina(.stamina - csc); // curse_stamina_cost
										o.AsUnit.Curse();
									}
							}
						}
					}
					//######### MODUL RETREAT 3 START ###########
					if(rand(10) == 1){ 
						if(o.AsUnit.hero.IsValid){
							if(o.AsUnit.hero.command == "retreat" ||
							   o.AsUnit.hero.command == "retreat_notai" ||
							   o.AsUnit.command == "retreat_bld_notai"){ 
								.Taunt(2000);
								Sleep(rand(100)+100);
								break;
							}
						} else {
							if(o.AsUnit.command == "retreat" ||
							   o.AsUnit.command == "retreat_notai" ||
							   o.AsUnit.command == "retreat_bld_notai"){
								.Taunt(2000);
								Sleep(rand(100)+100);
								break;
							}
						}
					}
					//######### MODUL RETREAT 3 END ###########
					if(.IsRanged){
						//######### MODUL GUARDIAN 1 START ###########
						if(o.AsUnit.IsHeirOf("EGuardian")){
							if(o.AsUnit.command != "engage" &&
							   o.AsUnit.command != "engage_unit_type"){
								if(rand(100) > 10){
									if(!o.AsUnit.BestTargetForPos.IsValid)
									if(o.AsUnit.health < o.AsUnit.maxhealth){
										o.AsUnit.Heal(500);
									}
								}
							}
						} 
						//######### MODUL GUARDIAN 1 END ###########
						
						//######### MODUL FIXBALL 1 START ###########
						if(!IsAIPlayer(.player)){
							olBall = Intersect(ObjsInCircle(.pos, bc, "Military"), ClassPlayerObjs("Military", .player)).GetObjList; 
							if(olBall.count >= 2){
								Sleep(olBall.count*1000); 
							}
						}
						//######### MODUL FIXBALL 1 END ###########
					}  
				}
			}
		}
		
		if(rand(10) == 1){
			
			if(o.AsUnit.hero.IsValid){
			
				if(o.AsUnit.hero.command == "retreat" ||
				   o.AsUnit.hero.command == "retreat_notai" ||
				   o.AsUnit.command == "retreat_bld_notai"){
				   
					.Taunt(2000);
					Sleep(rand(100)+100);
					break;
				}
				
			} else {
				if(o.AsUnit.command == "retreat" ||
				   o.AsUnit.command == "retreat_notai" ||
				   o.AsUnit.command == "retreat_bld_notai"){
				   
					.Taunt(2000);
					Sleep(rand(100)+100);
					break;
				}
			}
		}
		if(.IsValid){
			if(.IsRanged){
				if(.IsHeirOf("EChariot") && .stamina >= disease_cost){
					o = .BestTargetInSquadSight_PreferUndiseased();
				} else {
					o = .BestTargetInSquadSight(class);
				}
			} else {
				o = .BestTargetInRange( .posRH, 60 );
			}
			if(possibleSpeed)
				if(.speed_factor < 100)
					.SetSpeedFactor(100);
		}
	}
}